package com.heima.wemedia.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.feign.article.AricleFeign;
import com.heima.green.scan.GreenImageScan;
import com.heima.green.scan.GreenTextScan;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.wemedia.dto.*;
import com.heima.model.common.wemedia.pojo.*;
import com.heima.utils.common.UserContext;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.*;
import com.jlefebure.spring.boot.minio.MinioException;
import com.jlefebure.spring.boot.minio.MinioService;
import com.pojo.ScanResult;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.entity.ContentType;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;


import java.io.*;
import java.nio.file.Paths;
import java.util.*;

import java.util.stream.Collectors;

/**
 * <p>
 * 自媒体图文内容信息表 服务实现类
 * </p>
 *
 * @author author
 * @since 2023-06-03
 */
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmMaterialService wmMaterialService;
    @Autowired
    private WmNewsMaterialService wmNewsMaterialService;
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private MinioService minioService;
    @Autowired
    private AricleFeign aricleFeign;
    @Autowired
    private WmUserService wmUserService;
    @Autowired
    private WmChannelService wmChannelService;
    @Autowired
    private Configuration configuration;
    @Value("${spring.minio.url}")
    private String minioUrl;
    @Value("${spring.minio.bucket}")
    private String bucket;
//    @Autowired
//    private RabbitTemplate rabbitTemplate;

     @Autowired
     private StringRedisTemplate redisTemplate;

     @Autowired
     private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 分页查询自媒体文章
     */
    @Override
    public ResponseResult pager(WmNewPageDto dto) {
        dto.checkParam();
        IPage<WmNews> page =new Page<>(dto.getPage(),dto.getSize());
        LambdaQueryWrapper<WmNews> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(dto.getKeyword())){
            queryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }
        if (dto.getChannelId()!=null){
            queryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        if (dto.getStatus()!=null){
            queryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        if (dto.getBeginPubdate()!=null && dto.getEndPubdate()!=null){
            queryWrapper.between(WmNews::getPublishTime,dto.getBeginPubdate(),dto.getEndPubdate());
        }
        queryWrapper.eq(WmNews::getUserId, UserContext.getUserId());
        queryWrapper.orderByDesc(WmNews::getPublishTime);
        page(page,queryWrapper);
        List<WmNews> list = page.getRecords();
        long total = page.getTotal();
        return PageResponseResult.okResult(dto.getPage(),dto.getSize(),total,list);
    }

    /**
     * 添加修改自媒体文章
     */
    @Override
//    @Transactional
    @GlobalTransactional
    public ResponseResult submit(WmNewDto dto) throws Exception {
        //看存入草稿还是发布
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);
        //补充属性
        wmNews.setUserId(UserContext.getUserId().intValue());
        wmNews.setCreatedTime(new Date());
        //如果用户选择了自动就得从内容里边获取封面图
        //先拿封面
        String images = dto.getImages();
        List<String> list = JSON.parseArray(images, String.class);
        String join = StringUtils.join(list, ",");
        if (dto.getType()==WmNews.Type.AUTO){
            List<String> images1 = getcontentImages(dto.getContent());
            List<String> coverImage = getCoverImage(images1);
           join= StringUtils.join(coverImage, ",");
           wmNews.setType(coverImage.size());
        }
        wmNews.setImages(join);
        if (dto.getId() == null){
            save(wmNews);
        }else {
            updateById(wmNews);
        }


        //用户提交审核，再 wm_news_material中间表保存文章和素材有关信息
        if (dto.getStatus()== WmNews.Status.SUBMIT) {
            if (dto.getId()!=null){
                LambdaQueryWrapper<WmNewsMaterial> querryWraqqer =new LambdaQueryWrapper<>();
              querryWraqqer.eq(WmNewsMaterial::getNewsId,dto.getId());
                wmNewsMaterialService.remove(querryWraqqer);
            }
            saveWmNews(dto, wmNews);
            autoScan(wmNews);
        }
        return ResponseResult.okResult();
    }

    /**
     * 保存文章和素材的关系
     * @param dto
     * @param wmNews
     */
    private void saveWmNews(WmNewDto dto, WmNews wmNews) {
        //mp保存完毕后，自动的生成id封装WmNews对象
        //素材id获取
        Integer newsId = wmNews.getId();
        //获取内容url和封面url
        List<String> contentimages1 = getcontentImages(dto.getContent());
        String[] split = wmNews.getImages().split(",");
        List<String> coverImages = new ArrayList<>();
        Collections.addAll(coverImages, split);
        //建立素材和文章关系
        //根据素材url查询素材的id
        LambdaQueryWrapper<WmMaterial> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(WmMaterial::getUrl, contentimages1);
        List<WmMaterial> contentimages1list = wmMaterialService.list(queryWrapper);
        for (WmMaterial wmMaterial : contentimages1list) {
            Integer wmMaterialId = wmMaterial.getId();
            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
            wmNewsMaterial.setNewsId(newsId);
            wmNewsMaterial.setMaterialId(wmMaterialId);
            wmNewsMaterial.setType(false);
            wmNewsMaterialService.save(wmNewsMaterial);
        }
        LambdaQueryWrapper<WmMaterial> queryWrapper2 = new LambdaQueryWrapper<>();
        queryWrapper2.in(WmMaterial::getUrl, coverImages);
        List<WmMaterial> coverImageslist = wmMaterialService.list(queryWrapper2);
        for (WmMaterial wmMaterial : coverImageslist) {
            Integer wmMaterialId = wmMaterial.getId();
            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
            wmNewsMaterial.setNewsId(newsId);
            wmNewsMaterial.setMaterialId(wmMaterialId);
            wmNewsMaterial.setType(true);
            wmNewsMaterialService.save(wmNewsMaterial);
        }
    }

    /**
     * 根据id查询文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult fing(Integer id) {
        //根据文章id查文章
        WmNews wmNews = getById(id);
        if (wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        //登录用户id和所属id不同
        if (wmNews.getUserId().longValue()!= UserContext.getUserId()){
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }
        return ResponseResult.okResult(wmNews);
    }


    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult delete(Integer id) {
        WmNews wmNews = getById(id);
        if (wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        if (wmNews.getStatus() == WmNews.Status.PUBLISHED){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //登录用户id和所属id不同
        if (wmNews.getUserId().longValue()!= UserContext.getUserId()){
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        }
        //删除文章
        removeById(id);
        //删除文章和素材关系
        LambdaQueryWrapper<WmNewsMaterial> queryWraqqer = new LambdaQueryWrapper<>();
        queryWraqqer.eq(WmNewsMaterial::getNewsId,id);
        wmNewsMaterialService.remove(queryWraqqer);
        return ResponseResult.okResult();
    }


    //审核文本
    @Override
    public void autoScan(WmNews wmNews) throws Exception {
        //1.获取文章中的文本
        String contentText = getContentText(wmNews.getContent());
        //2 .调用阿里云内容安全服务审核文本
        ScanResult scanResult = greenTextScan.greeTextScan(contentText);
        boolean scanTextResult = scanText(wmNews, scanResult);
        if (!scanTextResult){
            return;
        }
        //4 文本审核通过，审核图片
        //5 获取图片中的url
        List<String> contentImages = getcontentImages(wmNews.getContent());
        boolean contentImageScan = contentImageScan(wmNews,contentImages);
        if (!contentImageScan){
            return;
        }
        String[] split = wmNews.getImages().split(",");
        List<String> coverImages = new ArrayList<>();
        Collections.addAll(coverImages,split);
        boolean coverImagesScan= contentImageScan(wmNews,coverImages);
        if (!coverImagesScan){
            return;
        }
        if (wmNews.getPublishTime().after(new Date())){
            wmNews.setStatus(WmNews.Status.SUCCESS);
            wmNews.setReason("文章转人工审核通过待发布");
            updateById(wmNews);
//            long ttl = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
//            Message message = MessageBuilder.withBody(String.valueOf(wmNews.getId()).getBytes())
//                    .setExpiration(String.valueOf(ttl))
//                    .build();
//            rabbitTemplate.convertAndSend("direct","blue",message);
            redisTemplate.opsForZSet().add("wmnews",String.valueOf(wmNews.getId()),wmNews.getPublishTime().getTime());
        }else {
            publishNews(wmNews);

        }
    }

    /**
     * 文章发布
     * @param wmNews
     * @throws Exception
     */
    @GlobalTransactional
    public void publishNews(WmNews wmNews) throws Exception {
        //否
        //远程调用文章微服务，保存用户端信息
        Long articleid = saveArticle(wmNews);
        //生成文章详情页面，保存再minio
        String staticUrl = genericStaticUrl(wmNews.getContent());
        //更新详情页面的地址到用户端文章表
        aricleFeign.updateStaticUrl(articleid,staticUrl);
        wmNews.setStatus(WmNews.Status.PUBLISHED);
        wmNews.setReason("审核通过，文章已发布");
        wmNews.setArticleId(articleid);
        updateById(wmNews);
    }

    /**
     * 查询自媒体文章状态为8，并且发布时间小于当前时间的文章
     */
    @Override
    public ResponseResult<List<WmNews>> findEnableNews() {
        LambdaQueryWrapper<WmNews> querywrapper = new LambdaQueryWrapper<>();
        querywrapper.eq(WmNews::getStatus,WmNews.Status.SUCCESS);
        querywrapper.lt(WmNews::getPublishTime,new Date());
        List<WmNews> newsList = list(querywrapper);
        return ResponseResult.okResult(newsList);
    }

    /**
     * 对指定文章进行发布
     * @param wmNews
     */
    @Override
    public ResponseResult publish(WmNews wmNews) throws Exception {
        publishNews(wmNews);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult<WmNews> getId(Integer id) {
        WmNews wmNews = getById(id);
        if (wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        return ResponseResult.okResult(wmNews);
    }

    /**
     *自媒体文章的上下架
     */
    @Override
    public ResponseResult dowmOrUp(WmNewsDownOrUpDto dto) {
        Integer id = dto.getId();
        WmNews wmNews = getById(id);
        Boolean enable = dto.getEnable();
        if (wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (wmNews.getStatus()!=WmNews.Status.PUBLISHED){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //对自媒体文章上下架
        wmNews.setEnable(dto.getEnable());
        updateById(wmNews);
        //发送消息到kafka
        Long articleId = wmNews.getArticleId();
        kafkaTemplate.send("new",String.valueOf(articleId),String.valueOf(enable));
        return ResponseResult.okResult();
    }


    /**
     * 生成文章详情页面，保存再minio
     * @param conent 文章内容
     * @throws IOException
     * @throws TemplateException
     * @throws MinioException
     */
    private String genericStaticUrl(String conent) throws Exception {
        //生成文章的详情页面，保存到minio
        File file = null;
        InputStream ios = null;
        Writer writer = null;
        try {
            List<ContentSegment> data = JSON.parseArray(conent, ContentSegment.class);
            Template template = configuration.getTemplate("article_content.ftl");
            Map<String,Object> map = new HashMap<>();
            map.put("data",data);
            String filename = UUID.randomUUID().toString() + ".html";
            String home = System.getProperty("user.home");
            file = new File(home+File.separator+filename);
           writer = new FileWriter(file);
            template.process(map,writer);
            writer.close();
            //讲详情页面保存到minio
            ios = new FileInputStream(file);
            minioService.upload(Paths.get(filename),ios, ContentType.TEXT_HTML);
            return minioUrl+"/"+bucket+"/"+filename;
        } finally {
            if (ios!=null){
                ios.close();
            }
            if (writer!=null){
                writer.close();
            }
            if (file!=null){
                file.delete();
            }
        }
    }


    /**
     *  远程调用文章微服务，保存用户端信息
     * @param wmNews
     */
    private Long saveArticle(WmNews wmNews) {
        WmNewsADto wmNewsADto = new WmNewsADto();
        BeanUtils.copyProperties(wmNews,wmNewsADto);
        WmUser wmUser = wmUserService.getById(wmNews.getUserId());
        wmNewsADto.setAuthorName(wmUser.getName());
        WmChannel channel = wmChannelService.getById(wmNews.getChannelId());
        wmNewsADto.setChannelName(channel.getName());
        ResponseResult<Long> responseResult = aricleFeign.saveArticle(wmNewsADto);
        return responseResult.getData();
    }

    private boolean contentImageScan(WmNews wmNews ,List<String> images) throws Exception {
        //6 从minio下载图片
        for (String contentImage : images){
            String name = FilenameUtils.getName(contentImage);
            InputStream inputStream = minioService.get(Paths.get(name));
            //7 .调用阿里云内容安全服务审核图片
            ScanResult imageScan = greenImageScan.imageScan(inputStream);
            String suggestion = imageScan.getSuggestion();
            if ("block".equals(suggestion)){
                wmNews.setStatus(WmNews.Status.FAIL);
                wmNews.setReason("文章图片有违规信息"+ imageScan.getLabel());
                updateById(wmNews);
                return false;
            }else if ("review".equals(suggestion)) {
                //审核不确定改3
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH);
                wmNews.setReason("文章转人工审核" + imageScan.getLabel());
                updateById(wmNews);
                return false;
            }
        }
        return true;
    }

    /**
     * 文章的文本审核
     * @param wmNews
     * @param scanResult
     * @return
     */
    private boolean scanText(WmNews wmNews, ScanResult scanResult) {
        String suggestion = scanResult.getSuggestion();
        if ("block".equals(suggestion)){
            //3 审核未通过，修改自媒体状态未2
            wmNews.setStatus(WmNews.Status.FAIL);
            wmNews.setReason("文章文本有违规信息"+ scanResult.getLabel());
            updateById(wmNews);
            return false;
        }
        return true;
    }

    /**
     * 获取文章内容中的文本
     * @param content
     * @return
     */
    private String getContentText(String content) {
        List<ContentSegment> contentSegments = JSON.parseArray(content, ContentSegment.class);
        StringBuilder stringBuilder = new StringBuilder();
        for (ContentSegment contentSegment : contentSegments) {
            String type = contentSegment.getType();
            String value = contentSegment.getValue();
            if (type.equals("text")){
                stringBuilder.append(value);
            }
        }
        return stringBuilder.toString();
    }


    //获取内容中图片所有地址
    private List<String> getcontentImages(String content){
    List<ContentSegment> segmentList = JSON.parseArray(content,ContentSegment.class);
    List<String> countImages = new ArrayList<>();
        for (ContentSegment contentSegment : segmentList) {
            if (contentSegment.getType().equals("image")){
                countImages.add(contentSegment.getValue());
            }
        }
        return countImages;
    }
    /**
     * 从内容获取封面图片
     */
    private List<String> getCoverImage(List<String> contentImage){
        if (contentImage.isEmpty()){
            return contentImage;
        }
        if (contentImage.size()<3){
            return contentImage.stream().limit(1).collect(Collectors.toList());
        }
        return contentImage.stream().limit(3).collect(Collectors.toList());
    }
}
